package cn.cas.mango.service;
import cn.cas.mango.dbscan.DBSCAN;
import cn.cas.mango.dbscan.Data;
import cn.cas.mango.dto.param.HitNews;
import cn.cas.mango.dto.param.HotKeywords;
import cn.cas.mango.dto.param.News;
import cn.cas.mango.util.GetLayerThreshold;
import co.elastic.clients.elasticsearch._types.SortOrder;

import lombok.extern.slf4j.Slf4j;

import java.util.*;

@Slf4j
public class SameNewsService {
  private final Map<String, HitNews> cast = new HashMap<>();
  private Map<String, Map<String, Double>> map2Score;

  private Map<String, Map<String, Double>> map2ExpandedScore;
  private final SortOrder sortOrder;
  private final String sortField;

  private final Integer range;
  private final List<HitNews> hits;
  // 计算相似度时是否统计分数
  private boolean addScore;

  private Integer minNeighbors;

  private Map<Integer, Map<String, List<String>>> map2Original;

  private int layer;
  public SameNewsService(List<HitNews> hits, SortOrder order, String sortField,
                         Integer range, boolean addScore, int layer,
                         Map<Integer, Map<String, List<String>>> map2Original) {
    this.hits = hits;
    this.sortOrder = order;
    this.sortField = sortField;
    this.range = range;
    this.addScore = addScore;
    this.layer = layer;
    this.map2Original = map2Original;
  }
  private void generateCast()  {
    for (HitNews hit: hits) {
      cast.put(hit.getNm(), hit);
    }
  }

  public Map<String, Map<String, Double>> getMap2ExpandedScore() {
    return map2ExpandedScore;
  }

  public Map<String, Map<String, Double>> getMap2Score() {
    return map2Score;
  }

  private Map<String, List<String>> runDBSCAN(List<HitNews> hits, double factor, Integer range,
                                              Map<String, Map<String, Integer>> clusterKeywordMap,
                                              Map<String, Set<String>> clusterEntitiesPerson,
                                              Map<String, Set<String>>  clusterEntitiesPlace,
                                              Map<String, Set<String>>  clusterEntitiesOrganization,
                                              Map<String, Set<String>>  clusterEntities,
                                              String v, Integer maxClusterSize) {
    List<Data> rawAllData = new ArrayList<>();
    for (HitNews hit1: hits) {
      rawAllData.add(wrappedData(hit1));
    }
    try {
      DBSCAN dbscan = new DBSCAN(rawAllData, range, clusterKeywordMap,
          clusterEntitiesPerson, clusterEntitiesPlace, clusterEntitiesOrganization,
          clusterEntities, new GetLayerThreshold().getMap().get(layer));
      Map<String, List<String>> run =
          dbscan.run(factor, v, maxClusterSize, minNeighbors, map2Original);
      map2Score = dbscan.getMap2Score();
      map2ExpandedScore = dbscan.getMap2ExpandedScore();
      return run;
    } catch (Exception e) {
      e.printStackTrace();
    }
    map2Score = new HashMap<>();
    map2ExpandedScore = new HashMap<>();
    return new HashMap<>();
  }

  private Data wrappedData(HitNews hit) {
    String id = hit.getNm();
    News news = hit;
    Double score = hit.getScore();
    Object keywordsObject = news.getKeywords();
    HotKeywords keywords =  new HotKeywords(keywordsObject);

    // 如果新闻时间空缺，指定一个默认时间
    String date = "2010-02-02";
    if (news.getFssj() != null) {
      date = news.getFssj();
    }
    List<List<String>> entitiesList = new ArrayList<>();
    List<Object> temp = new ArrayList<Object>() {{
      add(hit.getEntitiesPerson());add(hit.getEntitiesPlace());
      add(hit.getEntitiesOrganization()); add(hit.getEntities());}};
    for (Object t: temp) {
      if (t == null) {
        entitiesList.add(new ArrayList<>());
      } else {
        if (t instanceof String) {
          entitiesList.add(Collections.singletonList((String) t));
        } else if (t instanceof Set){
          entitiesList.add(new ArrayList<>((Set<String>)t));
        } else {
          entitiesList.add((List<String>)t);
        }
      }
    }
    return new Data(id, keywords, entitiesList, date, score, sortOrder, sortField, addScore, layer);
  }
  public Map<String, Map<String, Object>> getSameNewsList(double factor, String v, Integer maxClusterSize, Integer minNeighbors) {
    this.minNeighbors = minNeighbors;
    generateCast();
    Map<String, Map<String, Integer>> clusterKeywordMap = new HashMap<>();
    Map<String, Set<String>>  clusterEntitiesPerson = new HashMap<>();
    Map<String, Set<String>>  clusterEntitiesPlace = new HashMap<>();
    Map<String, Set<String>>  clusterEntitiesOrganization = new HashMap<>();
    Map<String, Set<String>>  clusterEntities = new HashMap<>();
    Map<String, List<String>> map = runDBSCAN(hits, factor, range, clusterKeywordMap,clusterEntitiesPerson,
        clusterEntitiesPlace, clusterEntitiesOrganization, clusterEntities, v, maxClusterSize);
    Map<String, Map<String, Object>> ret = new LinkedHashMap<>();
    for (String key: map.keySet()) {
      Map<String, Object> inner = new LinkedHashMap<>();

      ret.put(key, inner);
      List<HitNews> likeList = new ArrayList<>();
      for (String string: map.get(key)) {
        likeList.add(cast.get(string));
      }
      inner.put("like", likeList);
      ret.get(key).put("cluster_keywords", clusterKeywordMap.get(key));
      ret.get(key).put("cluster_entities_person", clusterEntitiesPerson.get(key));
      ret.get(key).put("cluster_entities_place", clusterEntitiesPlace.get(key));
      ret.get(key).put("cluster_entities_organization", clusterEntitiesOrganization.get(key));
      ret.get(key).put("cluster_entities", clusterEntities.get(key));
      ret.get(key).put("self", cast.get(key));
    }
    return ret;
  }
}
